package moodish.client;


import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.LinkedList;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

import moodish.comm.ClientComm;

public class GUI implements MoodishClient {
        private final String[] MOODS = { "Wonderful", "Loved", "Happy", "Good",
                        "Accepted", "Creative", "Cheerful", "Determined", "Content",
                        "At ease", "Challenged", "Careless", "Confused", "Clueless", "Shy",
                        "Annoyed", "Bored", "Sleepy", "Lonely", "Tired", "Needy",
                        "Uncomfortable", "Paranoid", "Hungry", "Disgruntled", "Cheated",
                        "Frazzled", "Run-down" };

        private JFrame window = new JFrame("Moodish");
        private final String[] CONNECT_DISCONNECT_VALUES = { "Disconnect",
                        "Connect" };
        private JButton connectDisconnect = new JButton(
                        CONNECT_DISCONNECT_VALUES[1]), friend = new JButton("Friend"),
                        unfriend = new JButton("Unfriend"), myMoods = new JButton(
                                        "My previous moods"), friendMoods = new JButton(
                                        "Friend's previous moods");
        private JComboBox<String> sendMood = new JComboBox<String>(MOODS);
        private JTabbedPane tabbedPane = new JTabbedPane();
        private ArrayList<MoodishFriend> friends = new ArrayList<MoodishFriend>();
        private ArrayList<String> connectedNicknames = new ArrayList<String>();
        private FriendsTableModel friendTableModel = new FriendsTableModel();
        private ConnectedTableModel connectedTableModel = new ConnectedTableModel();
        private JTable friendTable = new JTable(friendTableModel);
        private JTable connectedTable = new JTable(connectedTableModel);

        private String serverAddr = "", nickName = "";
        private int userCurrentMoodIndex = -1;
        private LinkedList<String> userMoods = new LinkedList<String>();

        private ClientComm clientComm;

        private MessageReader serverMessageInterpreter;
        private MoodsWindow userMoodsWindow;
        private MoodsWindow friendMoodsWindow;

        public GUI() {
                buildGUI();
                addListeners();

        }

        /**
         * starts the GUI
         */
        public void start(ClientComm clientComm) {
                this.clientComm = clientComm;
                window.setVisible(true);

        }

        /**
         * Adds the appropriate Listeners for each component for the user to interact with
         */
        private void addListeners() {
                //Window
                window.addWindowListener(new WindowListener() {
                        
                        @Override
                        public void windowOpened(WindowEvent e) {}
                        
                        @Override
                        public void windowIconified(WindowEvent e) {}
                        
                        @Override
                        public void windowDeiconified(WindowEvent e) {}
                        
                        @Override
                        public void windowDeactivated(WindowEvent e) {}
                        
                        @Override
                        public void windowClosing(WindowEvent e) {
                                if(clientComm.isConnected())
                                        clientComm.disconnect();
                        }
                        
                        @Override
                        public void windowClosed(WindowEvent e) {}
                        
                        @Override
                        public void windowActivated(WindowEvent e) {}
                });
                
                // Top Buttons
                connectDisconnect.addActionListener(new ActionListener() {

                        @Override
                        public void actionPerformed(ActionEvent e) {
                                connectDisconnect.setEnabled(false);
                                if (connectDisconnect.getText().equals(
                                                CONNECT_DISCONNECT_VALUES[1])) {
                                        serverAddr = JOptionPane.showInputDialog(window,
                                                        "Please type the Moodish server address:",
                                                        "Address", JOptionPane.INFORMATION_MESSAGE);
                                        if (serverAddr == null || serverAddr.trim().equals("")) {
                                                JOptionPane.showMessageDialog(window,
                                                                "Invalid server address.");
                                                connectDisconnect.setEnabled(true);
                                                return;
                                        }
                                        nickName = JOptionPane.showInputDialog(window,
                                                        "Please choose your nickname:", "nick",
                                                        JOptionPane.INFORMATION_MESSAGE);
                                        if (nickName == null || nickName.trim().equals("")) {
                                                JOptionPane.showMessageDialog(window,
                                                                "Invalid nickname.");
                                                connectDisconnect.setEnabled(true);
                                                return;
                                        }

                                        try {
                                                clientComm.connect(serverAddr, nickName);
                                               
                                                
                                                if(clientComm.isConnected()){
                                                	
                                                	System.err
															.println("nick name" + nickName);
                                                	System.out
															.println("IS CONNECTED? " + clientComm.isConnected());
                                                	
                                                connectDisconnect.setText(CONNECT_DISCONNECT_VALUES[0]);
                                                serverMessageInterpreter = new MessageReader(GUI.this,
                                                                clientComm);
                                                	
                                                new Thread(serverMessageInterpreter).start();
                                               
                                             
                                                	
                                                window.setTitle("Moodish - Connected as " + nickName);
                                                JOptionPane.showMessageDialog(window,
                                                                "Sucessfully connected as " + nickName);
                                                }else{
                                                	throw new IOException();
                                                }
                                        } catch (UnknownHostException e1) {
                                                JOptionPane.showMessageDialog(window, "Unknown host.");
                                        } catch (IOException e1) {
                                                JOptionPane.showMessageDialog(window,
                                                                "Could not connect to server");
                                        } finally {
                                                connectDisconnect.setEnabled(true);
                                        }
                                } else {
                                        clientComm.disconnect();
                                        connectionLost();
                                        connectDisconnect.setText(CONNECT_DISCONNECT_VALUES[1]);
                                        connectDisconnect.setEnabled(true);
                                }
                        }
                });

                sendMood.addItemListener(new ItemListener() {

                        @Override
                        public void itemStateChanged(ItemEvent e) {
                                if (e.getStateChange() == ItemEvent.SELECTED) {
                                        if (clientComm.isConnected()) {
                                                if (sendMood.getSelectedIndex() != userCurrentMoodIndex) {
                                                        int confirm = JOptionPane
                                                                        .showConfirmDialog(
                                                                                        window,
                                                                                        "Do you wish to change your mood to "
                                                                                                        + MOODS[sendMood
                                                                                                                        .getSelectedIndex()]
                                                                                                        + "?","Change Mood", JOptionPane.YES_NO_OPTION);
                                                        if (confirm == JOptionPane.OK_OPTION) {
                                                                userCurrentMoodIndex = sendMood
                                                                                .getSelectedIndex();
                                                                userMoods.addFirst(MOODS[userCurrentMoodIndex]);
                                                                clientComm
                                                                                .sendMoodishMessage(MOODS[userCurrentMoodIndex]);
                                                                if (userMoodsWindow != null)
                                                                        userMoodsWindow.update();
                                                        } else
                                                                sendMood.setSelectedIndex(userCurrentMoodIndex);
                                                }
                                        } else {
                                                JOptionPane.showMessageDialog(window,
                                                                "Cannot change your mood unless connected");
                                                sendMood.setSelectedIndex(-1);
                                        }
                                }

                        }
                });

                myMoods.addActionListener(new ActionListener() {

                        @Override
                        public void actionPerformed(ActionEvent e) {
                                if (userMoodsWindow != null)
                                        userMoodsWindow.getFrame().dispose();
                                userMoodsWindow = new MoodsWindow(userMoods, "Your Moods");
                        }
                });

                // Connected tab buttons
                friend.addActionListener(new ActionListener() {

                        @Override
                        public void actionPerformed(ActionEvent e) {
                                if (clientComm.isConnected()) {
                                        if (connectedTable.getSelectedRow() >= 0) {
                                                MoodishFriend newFriend = new MoodishFriend();
                                                newFriend.name = connectedNicknames.get(connectedTable
                                                                .getSelectedRow());
                                                boolean foundWithSameName = false;
                                                for (int i = 0; i < friends.size(); i++) {
                                                        if (friends.get(i).name.equals(newFriend.name))
                                                                foundWithSameName = true;
                                                }
                                                if (foundWithSameName)
                                                        JOptionPane.showMessageDialog(window,
                                                                        "Already a friend");
                                                else {
                                                        friends.add(newFriend);
                                                        clientComm.friendship(newFriend.name);
                                                        friendTableModel.fireTableDataChanged();
                                                        JOptionPane
                                                                        .showMessageDialog(window,
                                                                                        "You are now a friend of "
                                                                                                        + newFriend.name);
                                                }

                                        } else {
                                                JOptionPane.showMessageDialog(window,
                                                                "Please select a row");
                                        }
                                } else {
                                        JOptionPane.showMessageDialog(window,
                                                        "Please connect first");
                                }
                        }
                });

                // Friends tab buttons
                unfriend.addActionListener(new ActionListener() {

                        @Override
                        public void actionPerformed(ActionEvent e) {
                                if (clientComm.isConnected()) {
                                        if (friendTable.getSelectedRow() >= 0) {
                                                clientComm.unfriendship(friends.get(friendTable
                                                                .getSelectedRow()).name);
                                                friends.remove(friendTable.getSelectedRow());
                                        } else
                                                JOptionPane.showMessageDialog(window,
                                                                "Please select a row");
                                } else {
                                        JOptionPane.showMessageDialog(window,
                                                        "Please connect first");
                                }
                        }
                });

                friendMoods.addActionListener(new ActionListener() {

                        @Override
                        public void actionPerformed(ActionEvent e) {
                                if (clientComm.isConnected()) {
                                        if (friendTable.getSelectedRow() >= 0) {
                                                if (friendMoodsWindow != null)
                                                        friendMoodsWindow.getFrame().dispose();
                                                MoodishFriend friend = friends.get(friendTable
                                                                .getSelectedRow());
                                                friendMoodsWindow = new MoodsWindow(friend.moods,
                                                                friend.name + "'s Moods");
                                        } else
                                                JOptionPane.showConfirmDialog(window,
                                                                "Please select a row");
                                } else {
                                        JOptionPane.showConfirmDialog(window,
                                                        "Please connect first");
                                }

                        }
                });
        }

        /**
         * Builds the User Interface by putting each component where it belongs
         */
        private void buildGUI() {
                // Building top buttons
                JPanel topButtons = new JPanel(new FlowLayout(FlowLayout.LEFT));
                topButtons.add(connectDisconnect);
                sendMood.setSelectedIndex(userCurrentMoodIndex);
                topButtons.add(sendMood);
                topButtons.add(myMoods);

                // Building connected tab
                JPanel connectedPanel = new JPanel(new BorderLayout());
                connectedPanel.setName("Connected");
                connectedPanel.add(new JScrollPane(connectedTable));
                JPanel connectedButtons = new JPanel();
                connectedButtons.add(friend);
                connectedPanel.add(connectedButtons, BorderLayout.SOUTH);
                tabbedPane.add(connectedPanel);

                // Building friends tab
                JPanel friendPanel = new JPanel(new BorderLayout());
                friendPanel.setName("Friends");
                friendPanel.add(new JScrollPane(friendTable));
                JPanel friendButtons = new JPanel();
                friendPanel.add(friendButtons, BorderLayout.SOUTH);
                friendButtons.add(unfriend);
                friendButtons.add(friendMoods);
                tabbedPane.add(friendPanel);

                // Finalizing window
                window.add(topButtons, BorderLayout.NORTH);
                window.add(tabbedPane);
                window.setMinimumSize(new Dimension(390, 300));
                window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        }

        /**
         * Represents a friend of the user
         */
        private class MoodishFriend {
                String name = null;
                LinkedList<String> moods = new LinkedList<String>();
        }

        /**
         * Organizes a table so it displays MoodishFriends
         */
        private class FriendsTableModel extends AbstractTableModel {
                @Override
                public String getColumnName(int column) {
                        switch (column) {
                        case 0:
                                return "Nickname";
                        case 1:
                                return "Mood";
                        }
                        return null;
                }

                @Override
                public int getColumnCount() {
                        return 2;
                }

                @Override
                public int getRowCount() {
                        return friends.size();
                }

                @Override
                public Object getValueAt(int row, int column) {
                        switch (column) {
                        case 0:
                                return friends.get(row).name;
                        case 1:
                                if (friends.get(row).moods.size() > 0)
                                        return friends.get(row).moods.get(0);
                                else
                                        return "";
                        default:
                                return null;
                        }
                }

        }

        /**
         * Organizes a table so it displays Strings representing the nicknames of
         * the other users connected
         */
        private class ConnectedTableModel extends AbstractTableModel {
                @Override
                public String getColumnName(int column) {
                        return "Nickname";
                }

                @Override
                public int getColumnCount() {
                        return 1;
                }

                @Override
                public int getRowCount() {
                        return connectedNicknames.size();
                }

                @Override
                public Object getValueAt(int row, int column) {
                        return connectedNicknames.get(row);
                }

        }

        /**
         * Reset information related to the session. Call when connection to server
         * is lost
         */
        public void connectionLost() {
                window.setTitle("Moodish");
                connectDisconnect.setText("Connect");
                sendMood.setSelectedIndex(-1);
                friends = new ArrayList<MoodishFriend>();
                connectedNicknames = new ArrayList<String>();
                friendTableModel.fireTableDataChanged();
                connectedTableModel.fireTableDataChanged();
        }

        /**
         * 
         * @return nickname
         */
        public String getUserNickName() {
                return nickName;
        }

        /**
         * Adds the given nickname to the connected users
         * 
         * @param payLoad
         */
        public void userConnected(String nickname) {
                if (nickname == null || nickname.equals(""))
                        throw new IllegalArgumentException("Invalid nickname");
                connectedNicknames.add(nickname);
                connectedTableModel.fireTableDataChanged();
        }

        /**
         * Removes the user with the given nickname from friends and from connected
         * 
         * @param nickname
         */
        public void userDisconnected(String nickname) {
                for (MoodishFriend f : friends) {
                        if (f.name.equals(nickname)) {
                                friends.remove(f);
                                friendTableModel.fireTableDataChanged();
                                break;
                        }
                }
                for (String c : connectedNicknames) {
                        if (c.equals(nickname)) {
                                connectedNicknames.remove(c);
                                connectedTableModel.fireTableDataChanged();
                                break;
                        }
                }
        }

        /**
         * Changes the mood of a friend, if existent
         * 
         * @param nickName
         * @param mood
         */
        public void setMood(String nickName, String mood) {
                for (int i = 0; i < friends.size(); i++)
                        if (friends.get(i).name.equals(nickName)) {
                                friends.get(i).moods.addFirst(mood);
                                friendTableModel.fireTableDataChanged();
                                break;
                        }
                if (friendMoodsWindow != null)
                        friendMoodsWindow.update();
        }

        /**
         * 
         * @return JFrame
         */
        public JFrame getJFrame() {
                return window;
        }

}